package test;

import java.util.*;

public class N_Gram {
    /**
     * 生成n元语法
     * <p>
     * 一个句子中有多少个N-gram?
     * 如果 X = 给定句子K中的单词数，则句子K的 N-gram数为:
     * N(grams<K>) = X - (N - 1)
     *
     * @param n        连续 n个单词
     * @param sentence 句子级别的文本
     * @return         存着ngram的列表
     */
    public static List<String> ngrams(int n, String sentence) {
        List<String> ngrams = new ArrayList<>();
        String[] words = sentence.split("\\s+");
        for (int i = 0; i < words.length - n + 1; i++)
            ngrams.add(concat(words, i, i + n));
        return ngrams;
    }

    /**
     * 拼接单词
     *
     * @param words 单词
     * @param start 开始位置
     * @param end   结束位置
     * @return      拼接好的字符串
     */
    public static String concat(String[] words, int start, int end) {
        StringBuilder sb = new StringBuilder();
        for (int i = start; i < end; i++)
            sb.append(i > start ? " " : "").append(words[i]);
        return sb.toString();
    }

    /**
     * 按 value对 HashMap进行逆序排序
     * <p>
     * 使用 Java 8 Stream API按照降序对Value进行Map排序
     * 逻辑的中心是按自然顺序 Map.Entry.comparingByValue()比较 Map.Entry值的方法。
     *
     * @param unSortedMap 未排序的HashMap
     * @return 按照value降序排序的HashMap
     */
    public static HashMap<String, Integer> sortByValue(HashMap<String, Integer> unSortedMap) {
        // System.out.println("Unsorted Map : " + unSortedMap);

        // LinkedHashMap保留插入元素的顺序
        LinkedHashMap<String, Integer> reverseSortedMap = new LinkedHashMap<>();

        // 使用 Comparator.reverseOrder() 进行反向排序
        unSortedMap.entrySet()
                .stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .forEachOrdered(x -> reverseSortedMap.put(x.getKey(), x.getValue()));

        // System.out.println("Reverse Sorted Map   : " + reverseSortedMap);

        return reverseSortedMap;
    }

    public static void main(String[] args) {
        HashMap<String, Integer> count = new HashMap<>();
        String text = "程作为整体考察时所产生的各种具体 形式。资本在自己的现实 运动中就是以这些具 体形式互相对立的。对这些具体 形式来说，资本在直接生产过程中采取的形态和在流通过程中采取的形态，只是表现为特殊的要素。因此，我们在本卷中将要阐明的资本的各种形式，同资本在社会表面上，在各种资本的互相作用中，在竞争中，以及在生产当事人自己的通常意识中所表现出来的形式，是一步一步地接近了。\n" +
                "\n商品的资本主义费用是用资本的耗费来计量 的，而商 品的实际费用则是 用劳动的耗费来计量 的。歧义之所在。所以 ，商品的 资本主义的成本价格，在数量上是与商品的价值或商品的实际成本价格不同的；它小于商品价值，因为，既然W=k+m，那末k=W-m。另一方面，商品的成本价格也决不是一个仅仅存在于资本家账簿上的项目。这个价值部分的独立存在，在现实的商品生产中，会经常发生实际的影响，因为这个价值部分会通";

        // 生成n为1~3的N元语法
//        for (int n = 1; n <= 3; n++) {
//            for (String ngram : ngrams(n, text)) {
//                System.out.println(ngram);
//            }
//            System.out.println();
//        }

        for (String ngram : ngrams(3, text)) {
            // counting ngram by using HashMap
            if (!count.containsKey(ngram)) {
                count.put(ngram, 1);
            } else if (count.containsKey(ngram)) {
                count.replace(ngram, count.get(ngram) + 1);
            }
            System.out.println(ngram);
        }

        // 按出现次由多到少的顺序打印ngram
        System.out.println("\nCounting Result: ");
        for (Map.Entry<String, Integer> entry : sortByValue(count).entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

    }
}
